package com.rybbaby.tss.core.utils;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.rybbaby.tss.core.utils.pojo.TreeFile;
import org.apache.commons.io.FileExistsException;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;

import java.io.*;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

/**
 * @author 谢进伟
 * @description 文件读取工具 , 扩展Apache文件工具库
 * @createDate 2016年2月11日 下午1:11:07
 */
public class FileUtils extends org.apache.commons.io.FileUtils {

    private static ObjectMapper om = new ObjectMapper();
    // 缓存文件头信息-文件头信息
    public static final HashMap<String, String> mFileTypes = new HashMap<String, String>();

    static {
        // images
        mFileTypes.put("FFD8FF", "jpg");
        mFileTypes.put("89504E47", "png");
        mFileTypes.put("47494638", "gif");
        mFileTypes.put("49492A00", "tif");
        mFileTypes.put("424D", "bmp");
        //
        mFileTypes.put("41433130", "dwg"); // CAD
        mFileTypes.put("38425053", "psd");
        mFileTypes.put("7B5C727466", "rtf"); // 日记本
        mFileTypes.put("3C3F786D6C", "xml");
        mFileTypes.put("68746D6C3E", "html");
        mFileTypes.put("44656C69766572792D646174653A", "eml"); // 邮件
        mFileTypes.put("D0CF11E0", "doc");
        mFileTypes.put("5374616E64617264204A", "mdb");
        mFileTypes.put("252150532D41646F6265", "ps");
        mFileTypes.put("255044462D312E", "pdf");
        mFileTypes.put("504B0304", "docx");
        mFileTypes.put("52617221", "rar");
        mFileTypes.put("57415645", "wav");
        mFileTypes.put("41564920", "avi");
        mFileTypes.put("2E524D46", "rm");
        mFileTypes.put("000001BA", "mpg");
        mFileTypes.put("000001B3", "mpg");
        mFileTypes.put("6D6F6F76", "mov");
        mFileTypes.put("3026B2758E66CF11", "asf");
        mFileTypes.put("4D546864", "mid");
        mFileTypes.put("1F8B08", "gz");
    }

    /**
     * 重命名文件
     *
     * @param file        需要重命名的文件
     * @param newFileName 新文件名
     * @return
     * @throws FileNotFoundException
     * @throws FileExistsException
     */
    public static boolean rename(File file, String newFileName) throws FileNotFoundException, FileExistsException {
        if (!file.exists()) {
            throw new FileNotFoundException("没有发现文件：" + file.getPath());
        }
        if (StringUtils.isBlank(newFileName)) {
            throw new IllegalArgumentException("新文件名不能为空");
        }
        if (StringUtils.indexOf(newFileName, ".") == -1) {
            String suffix = StringUtils.substringAfterLast(file.getName(), ".");
            newFileName = newFileName + "." + suffix;
        }
        File newFile = new File(file.getParentFile().getPath() + "/" + newFileName);
        if (newFile.exists()) {
            throw new FileExistsException("文件" + newFile.getPath() + "已经存在");
        }
        return file.renameTo(newFile);
    }

    /**
     * 压缩文件
     *
     * @param srcfile 需要压缩的文件列表
     * @param zipfile 压缩后的文件
     */
    public static void zip(List<File> srcfile, File zipfile) {
        byte[] buf = new byte[1024];
        try {
            if (srcfile != null && !srcfile.isEmpty()) {
                ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipfile));
                for (File file : srcfile) {
                    FileInputStream in = new FileInputStream(file);
                    out.putNextEntry(new ZipEntry(file.getName()));
                    int len;
                    while ((len = in.read(buf)) > 0) {
                        out.write(buf, 0, len);
                    }
                    IOUtils.closeQuietly(in);
                }
                IOUtils.closeQuietly(out);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 解压文件到压缩文件当前目录
     *
     * @param zipfile 压缩文件
     */
    public static void unZip(File zipfile) {
        unZip(zipfile, null);
    }

    /**
     * 解压缩
     *
     * @param zipfile 需要解压缩的文件
     * @param descDir 解压后的目标目录
     */
    public static void unZip(File zipfile, String descDir) {
        try {
            String zipfileParentFilePath = zipfile.getParentFile().getPath();
            if (StringUtils.isBlank(descDir)) {
                descDir = zipfileParentFilePath;
            }
            File descDirFile = new File(descDir);
            if (!descDirFile.exists()) {
                //创建解压目录
                descDirFile.mkdirs();
            }
            ZipFile zf = new ZipFile(zipfile);
            for (Enumeration entries = zf.entries(); entries.hasMoreElements(); ) {
                ZipEntry entry = ((ZipEntry) entries.nextElement());
                if (entry.isDirectory()) {//文件夹
                    File childrenDir = new File(descDir + File.separator + entry.getName());
                    if (!childrenDir.exists()) {
                        childrenDir.mkdirs();
                    }
                } else {//文件
                    String zipEntryName = entry.getName();
                    InputStream in = null;
                    FileOutputStream out = null;
                    try {
                        in = zf.getInputStream(entry);
                        File f = new File(descDir + File.separator + zipEntryName);
                        out = new FileOutputStream(f);
                        IOUtils.copy(in, out);
                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {
                        IOUtils.closeQuietly(in);
                        IOUtils.closeQuietly(out);
                    }
                }
            }
            zf.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取指定文件夹中的所有文件（包含所有子文件夹中的文件）
     *
     * @param dir       文件夹
     * @param listFiles 最后存放文件的集合
     */
    public static void getChildrenFiles(File dir, List<File> listFiles) {
        File[] childrenFiles = dir.listFiles();
        for (File file : childrenFiles) {
            if (file.isDirectory()) {
                getChildrenFiles(file, listFiles);
            } else {
                listFiles.add(file);
            }
        }
    }

    /**
     * 根据文件路径获取文件头信息
     *
     * @param file 文件
     * @return 文件类型
     */
    public static String getFileType(File file) {
        return mFileTypes.get(getFileHeader(file));
    }

    /**
     * 根据文件路径获取文件头信息
     *
     * @param file 文件
     * @return 文件头信息
     */
    private static String getFileHeader(File file) {
        FileInputStream is = null;
        String value = null;
        try {
            is = new FileInputStream(file);
            byte[] b = new byte[4];
            is.read(b, 0, b.length);
            value = bytesToHexString(b);
        } catch (Exception e) {
        } finally {
            if (null != is) {
                try {
                    is.close();
                } catch (IOException e) {
                }
            }
        }
        return value;
    }

    /**
     * 将要读取文件头信息的文件的byte数组转换成string类型表示
     *
     * @param src 要读取文件头信息的文件的byte数组
     * @return 文件头信息
     */
    private static String bytesToHexString(byte[] src) {
        StringBuilder builder = new StringBuilder();
        if (src == null || src.length <= 0) {
            return null;
        }
        String hv;
        for (int i = 0; i < src.length; i++) {
            // 以十六进制（基数 16）无符号整数形式返回一个整数参数的字符串表示形式，并转换为大写
            hv = Integer.toHexString(src[i] & 0xFF).toUpperCase();
            if (hv.length() < 2) {
                builder.append(0);
            }
            builder.append(hv);
        }
        System.out.println(builder.toString());
        return builder.toString();
    }

    /**
     * 读取指定目录一级子目录结构,并将目录结构转换成json字符串
     *
     * @param dir           文件目录
     * @param isIncludeFile 是否包含文件
     * @return
     * @throws JsonProcessingException
     */
    public static String readChildrenFileToJsonStr(String dir, boolean isIncludeFile) throws JsonProcessingException {
        File[] listfile = null;
        if (StringUtils.isNotBlank(dir)) {
            File file = new File(dir);
            listfile = file.listFiles();
        } else {
            listfile = File.listRoots();
        }
        if (listfile == null) {
            listfile = new File[]{};
        }
        sort(listfile);
        String dirUUID = getUUID();
        StringBuffer dataSb = new StringBuffer();
        dataSb.append("[");
        for (File file : listfile) {
            boolean isRooltFile = file.getParent() == null;
            boolean directory = file.isDirectory();
            boolean isFile = file.isFile();
            if ((!isIncludeFile && isFile) || (!isRooltFile && file.isHidden()) || !file.canRead() || !file.canWrite()) {
                continue;
            }
            String uuid = getUUID();
            String path = file.getPath();
            String parent = file.getParent();
            TreeFile tf = new TreeFile(uuid, dirUUID, parent, path, directory);
            String name = file.getName();
            String absolutePath = file.getAbsolutePath().replaceAll(":\\\\", " 盘");
            dataSb.append("{ name: \"" + (StringUtils.isNotBlank(name) ? name : absolutePath) + "\", type: \"" + (isFile ? "item" : "folder") + "\", params: " + om.writeValueAsString(tf).replace("\"", "\""));
            dataSb.append(",data: [");
            dataSb.append("]},");
        }
        if (dataSb.length() > 1) {
            dataSb.deleteCharAt(dataSb.length() - 1);
        }
        dataSb.append("]");
        return dataSb.toString();
    }

    /**
     * 读取指定目录整体结构,并将目录结构转换成json字符串
     *
     * @param projectDir    文件目录
     * @param isIncludeFile 是否包含文件
     * @return
     * @throws JsonProcessingException
     */
    public static String readFileToJsonStr(String projectDir, boolean isIncludeFile) throws JsonProcessingException {
        File project = new File(projectDir);
        String dirUUID = getUUID();
        StringBuffer data = new StringBuffer();
        data.append("[");
        redFileToJsonStr(project, dirUUID, data, isIncludeFile);
        if (data.length() > 1) {
            data.deleteCharAt(data.length() - 1);
        }
        data.append("]");
        return data.toString();
    }

    /**
     * 递归读取指定目录,并将目录结构转换成json字符串
     *
     * @param file          文件
     * @param dirUUID       文件所在目录唯一标识
     * @param dataSb        数据容器
     * @param isIncludeFile 是否包含文件
     * @throws JsonProcessingException
     */
    private static void redFileToJsonStr(File file, String dirUUID, StringBuffer dataSb, boolean isIncludeFile) throws JsonProcessingException {
        if (file.getName().startsWith(".") || file.getName().startsWith("target") || (!isIncludeFile && file.isFile())) {
            return;
        }
        boolean directory = file.isDirectory();
        boolean isFile = file.isFile();
        boolean isRooltFile = file.getParent() == null;
        String uuid = getUUID();
        String path = file.getPath();
        String parent = file.getParent();
        TreeFile tf = new TreeFile(uuid, dirUUID, parent, path, directory);
        dataSb.append("{ name: \"" + file.getName() + "\", type: \"" + (file.isFile() ? "item" : "folder") + "\", params: " + om.writeValueAsString(tf).replace("\"", "\""));
        dataSb.append(",data: [");
        if (directory) {
            dirUUID = getUUID();
            File[] listFiles = file.listFiles();
            if (listFiles == null) {
                return;
            }
            sort(listFiles);
            int length = listFiles.length;
            for (int i = 0; i < length; i++) {
                if ((!isIncludeFile && isFile) || (!isRooltFile && file.isHidden()) || (isFile && (!file.canExecute())) || !file.canRead() || !file.canWrite()) {
                    continue;
                }
                File childrenFile = listFiles[i];
                redFileToJsonStr(childrenFile, dirUUID, dataSb, isIncludeFile);
            }
        }
        dataSb.append("]},");
    }

    /**
     * 文件排序
     *
     * @param fileList 需要排序的文件组
     */
    private static void sort(File[] fileList) {
        if (fileList == null) {
            return;
        }
        int length = fileList.length;
        if (length > 0) {
            Arrays.sort(fileList, 0, length, (File f1, File f2) -> {
                        Boolean b1 = f1.isDirectory();
                        Boolean b2 = f2.isDirectory();
                        if (b1 && !b2) {
                            return -1;
                        } else if (!b1 && b2) {
                            return 1;
                        } else {
                            return f1.compareTo(f2);
                        }
                    }
            );
        }
    }

    public static String convertFileSize(double size) {
        Double kb = 1024D;//1024
        Double mb = kb * 1024;//1024 * 1024
        Double gb = mb * 1024;//1024 * 1024 * 1024
        Double tb = gb * 1024;//1024 * 1024 * 1024 * 1024
        Double pb = tb * 1024;//1024 * 1024 * 1024 * 1024 * 1024
        Double eb = pb * 1024;//1024 * 1024 * 1024 * 1024 * 1024 * 1024
        Double zb = eb * 1024;//1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024
        Double yb = zb * 1024;//1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024
        if (size >= yb) {
            return String.format("%.1f YB", size / yb);
        } else if (size >= zb) {
            return String.format("%.1f ZB", size / zb);
        } else if (size >= eb) {
            return String.format("%.1f EB", size / eb);
        } else if (size >= pb) {
            return String.format("%.1f PB", size / pb);
        } else if (size >= tb) {
            return String.format("%.1f TB", size / tb);
        } else if (size >= gb) {
            return String.format("%.1f GB", size / gb);
        } else if (size >= mb) {
            Double f = size / mb;
            return String.format(f > 100 ? "%.0f MB" : "%.1f MB", f);
        } else if (size >= kb) {
            Double f = size / kb;
            return String.format(f > 100 ? "%.0f KB" : "%.1f KB", f);
        } else {
            return String.format("%.1f B", size);
        }
    }

    /**
     * 生成UUID
     *
     * @return
     */
    private static String getUUID() {
        return SequenceUtil.getUUID();
    }

    public static void main(String[] args) {
        System.out.println(FileUtils.convertFileSize(200D));
        System.out.println(FileUtils.convertFileSize(1024D));
        System.out.println(FileUtils.convertFileSize(1024 * 1024D));
        System.out.println(FileUtils.convertFileSize(1024D * 1024 * 1024));
        System.out.println(FileUtils.convertFileSize(1024D * 1024 * 1024 * 1024));
        System.out.println(FileUtils.convertFileSize(1024D * 1024 * 1024 * 1024 * 1024));
        System.out.println(FileUtils.convertFileSize(1024D * 1024 * 1024 * 1024 * 1024 * 1024));
        System.out.println(FileUtils.convertFileSize(1024D * 1024 * 1024 * 1024 * 1024 * 1024 * 1024));
        System.out.println(FileUtils.convertFileSize(1024D * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024));

        String str[] = {"WEB-INF", "asset", "META-INF"};
        Arrays.sort(str, (String str1, String str2) -> {
            int i = str2.compareTo(str1);
            System.out.println(i);
            return i;
        });
        for (String s : str) {
            System.out.println(s);
        }


        File f = new File("E:/weixinpay.zip");
        //FileUtils.unZip(f, "E:/demos");
        FileUtils.unZip(f);
    }
}
